/*
 * PathUtil.cpp
 *
 *  Created: 2011-10-21
 *   Author: terry
 */

#include "stdafx.h"
#include "PathUtil.h"

#include <shlobj.h>
#include <stdio.h>


bool PathUtil::exists(const string& PathUtil)
{
    return (GetFileAttributes(PathUtil.c_str()) != 0xFFFFFFFF);
}


bool PathUtil::createDirectory(const string& PathUtil)
{
    return (::CreateDirectory(PathUtil.c_str(), NULL) != FALSE);
}


static bool createIfNotExist(const char* PathUtil)
{
    BOOL done = TRUE;
    DWORD attr = ::GetFileAttributes(PathUtil);
    if (attr == 0xFFFFFFFF)
    {
        done = CreateDirectory(PathUtil, NULL);
    }
    return (done != 0);
}

bool PathUtil::createDirectories(const string& PathUtil)
{
    if (PathUtil.empty())
    {
        return false;
    }

    char buffer[MAX_PATH] = {0};
    #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
    strncpy_s(buffer, MAX_PATH, PathUtil.c_str(), PathUtil.size());
    #else
    strncpy(buffer, PathUtil.c_str(), PathUtil.size());
    #endif //_MSC_VER
    
    

    for (size_t i = 0; i < PathUtil.size(); ++ i)
    {
        if (buffer[i] == PATH_SPERATOR)
        {
            buffer[i] = '\0';

            if (!createIfNotExist(buffer))
            {
                    return false;
            }

            buffer[i] = PATH_SPERATOR;
        }
    }

    if (PathUtil[PathUtil.size() - 1] == PATH_SPERATOR)
    {
        return true;
    }

    return createIfNotExist(buffer);
}

bool PathUtil::isDir(const string& PathUtil)
{
    DWORD attr = GetFileAttributes(PathUtil.c_str());
    return (attr != 0xFFFFFFFF) && ((attr & FILE_ATTRIBUTE_DIRECTORY) > 0);
}

bool PathUtil::isFile(const string& PathUtil)
{
    DWORD attr = GetFileAttributes(PathUtil.c_str());
    return (attr != 0xFFFFFFFF) && ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0);
}

bool PathUtil::isLink(const string& PathUtil)
{
    return splitext(PathUtil).second == ".lnk";
}


class AutoHandle
{
public:
    explicit AutoHandle(const string& PathUtil):
        m_handle(INVALID_HANDLE_VALUE)
    {
        m_handle = CreateFile(PathUtil.c_str(), GENERIC_READ,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    }

    explicit AutoHandle(HANDLE handle):
            m_handle(handle)
    {
    }

    ~AutoHandle()
    {
        if (m_handle != INVALID_HANDLE_VALUE)
        {
            ::CloseHandle(m_handle);
        }
    }

    bool operator ! () const
    {
        return (m_handle == INVALID_HANDLE_VALUE);
    }

    operator bool () const
    {
        return (m_handle != INVALID_HANDLE_VALUE);
    }

    HANDLE  m_handle;

};

time_t filetime_to_timet(const FILETIME& ft)
{
    ULARGE_INTEGER ull;
    ull.LowPart = ft.dwLowDateTime;
    ull.HighPart = ft.dwHighDateTime;
    return ull.QuadPart / 10000000ULL - 11644473600ULL;
}


time_t PathUtil::getatime(const string& PathUtil)
{
    AutoHandle handle(PathUtil);
    FILETIME ft;
    memset(&ft, 0, sizeof(ft));
    GetFileTime(handle.m_handle, NULL, &ft, NULL);
    return filetime_to_timet(ft);
}

time_t PathUtil::getmtime(const string& PathUtil)
{
    AutoHandle handle(PathUtil);
    FILETIME ft;
    memset(&ft, 0, sizeof(ft));
    GetFileTime(handle.m_handle, NULL, NULL, &ft);
    return filetime_to_timet(ft);
}

time_t PathUtil::getctime(const string& PathUtil)
{
    AutoHandle handle(PathUtil);
    FILETIME ft;
    memset(&ft, 0, sizeof(ft));
    GetFileTime(handle.m_handle, &ft, NULL, NULL);
    return filetime_to_timet(ft);
}


__int64 PathUtil::getSize(const string& PathUtil)
{
    AutoHandle handle(PathUtil);
    LARGE_INTEGER li;
    li.LowPart = GetFileSize(handle.m_handle, (DWORD*)&li.HighPart);

    return li.QuadPart;
}



bool PathUtil::isValidFileName(const string& filename)
{
    if (filename.empty())
    {
        return false;
    }

    char excludes[] = {'/', '\\', '|', '?', '<', '>', '*', '"', ':' };
    for (size_t i = 0; i < sizeof(excludes)/sizeof(char); ++ i)
    {
        if (filename.find(excludes[i]) >= 0)
        {
            return false;
        }
    }
    return true;
}

string PathUtil::abspath(const string& PathUtil)
{
    char buffer[MAX_PATH * 2] = {0};
    char* lpFilePart = NULL;
    GetFullPathName(PathUtil.c_str(), MAX_PATH * 2, buffer, &lpFilePart);
    return string(buffer);
}

bool PathUtil::isabs(const string& PathUtil)
{
    bool exp = false;
    if (PathUtil.size() >= 3)
    {
        exp = (PathUtil[1] == ':') && (PathUtil[2] == '\\');
    }
    return exp;
}


string PathUtil::normpath(const string& PathUtil)
{
    string result(PathUtil);
    toWindowsSlash(result);

    return abspath(result);
}



string PathUtil::getCurDir()
{
    char buffer[MAX_PATH] = {0};
    DWORD count = ::GetCurrentDirectory(MAX_PATH, buffer);
    if (count > 0)
    {
        return string(buffer, count);
    }
    return string();
}

string PathUtil::getWorkDir()
{
    char buffer[MAX_PATH] = {0};
    ::GetModuleFileName(NULL, buffer, MAX_PATH);
    return dirname(buffer);
}


string PathUtil::getWindowDir()
{
    char buffer[MAX_PATH] = {0};
    UINT count = ::GetWindowsDirectory(buffer, MAX_PATH);
    return string(buffer, count);
}

string PathUtil::getWindowDisk()
{
    char buffer[MAX_PATH] = {0};
    ::GetWindowsDirectory(buffer, MAX_PATH);
    return string(buffer, 3);
}

string PathUtil::getSystemDir()
{
    char buffer[MAX_PATH] = {0};
    UINT count = ::GetSystemDirectory(buffer, MAX_PATH);
    return string(buffer, count);
}

string PathUtil::getSystem32Dir()
{
    string PathUtil = getWindowDir();
    PathUtil += "\\system32";
    return PathUtil;
}


bool PathUtil::listDir(const string& dir, std::vector< string >& array)
{
    string filename = join(dir, "*");
    WIN32_FIND_DATA filedata;
    HANDLE hSearch = FindFirstFile(filename.c_str(), &filedata);
    if (hSearch == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    BOOL working = true;
    while (working)
    {
        bool doing = true;
        if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
        {
            string name(filedata.cFileName);
            if (name.compare(".") == 0)
            {
                doing = false;
            }
            else if (name.compare("..") == 0)
            {
                doing = false;
            }
        }
        if (doing)
        {
            array.push_back(filedata.cFileName);
        }
        working = FindNextFile(hSearch, &filedata);
    }

    FindClose(hSearch);

    return true;
}

bool PathUtil::listFile(const string& dir, std::vector< string >& array)
{
    string filename = join(dir, "*");
    WIN32_FIND_DATA filedata;
    HANDLE hSearch = FindFirstFile(filename.c_str(), &filedata);
    if (hSearch == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    BOOL working = true;
    while (working)
    {
        if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
        {
            //pass
        }
        else
        {
            array.push_back(filedata.cFileName);
        }
        
        working = FindNextFile(hSearch, &filedata);
    }

    FindClose(hSearch);

    return true;
}


string PathUtil::getHomeDir()
{
	char buffer[MAX_PATH] = {0};
	SHGetFolderPath(NULL, CSIDL_PERSONAL|CSIDL_FLAG_CREATE,
            NULL,
            0,
            buffer);
	return string(buffer);
}





string PathUtil::basename(const string& path)
{
    size_t pos = path.rfind(PATH_SPERATOR);
    if (pos == string::npos)
    {
        return path;
    }
    return path.substr(pos + 1);
}

string PathUtil::dirname(const string& path)
{
    size_t pos = path.rfind(PATH_SPERATOR);
    if (pos == string::npos)
    {
        return string();
    }
    return path.substr(0, pos);
}


string PathUtil::join(const string& dir, const string& filename)
{
    if (dir.empty())
    {
        return filename;
    }
    if (filename.empty())
    {
        return dir;
    }

    string path(dir);
    if (path[path.length() - 1] != PATH_SPERATOR)
    {
        if (!filename.empty() && filename[0] != PATH_SPERATOR)
        {
            path += PATH_SPERATOR;
        }
    }
    path += filename;
    return path;
}

string PathUtil::getFileName(const string& path)
{
    return basename(path);
}

string PathUtil::getFileExt(const string& filename)
{
    size_t pos = filename.rfind('.');
    if (pos == string::npos)
    {
        return string();
    }
    return filename.substr(pos);
}

string PathUtil::getFileTitle(const string& filename)
{
    size_t pos = filename.rfind('.');
    if (pos == string::npos)
    {
        return filename;
    }
    return filename.substr(0, pos);
}

std::pair< string, string > PathUtil::split(const string& path)
{
    size_t pos = path.rfind(PATH_SPERATOR);
    if (pos == string::npos)
    {
        return std::pair< string, string >(string(), path);
    }
    return std::pair< string, string >(path.substr(0, pos), path.substr(pos+1));
}

std::pair< string, string > PathUtil::splitext(const string& path)
{
    size_t pos = path.rfind('.');
    if (pos == string::npos)
    {
        return std::pair< string, string >(path, string());
    }
    return std::pair< string, string >(path.substr(0, pos), path.substr(pos));
}







bool PathUtil::addSperator(string& path)
{
    if (path.empty())
    {
        return false;
    }

    bool done = false;
    if (path[path.length() - 1] != PATH_SPERATOR)
    {
        path += PATH_SPERATOR;
        done = true;
    }
    return done;
}

void PathUtil::removeSperator(string& path)
{
    if (path.empty())
    {
        return;
    }

    if (path[path.length() - 1] != PATH_SPERATOR)
    {
        path.erase(path.length() - 1, 1);
    }
}

bool PathUtil::endWithSperator(const string& path)
{
    return (!path.empty() && (path[path.length()-1] == PATH_SPERATOR));
}


void PathUtil::convertSlash(string& path)
{
	toWindowsSlash(path);
}

void PathUtil::toWindowsSlash(string& path)
{
    for (size_t i = 0; i < path.size(); ++ i)
    {
        if (path[i] == FORWARD_SLASH)
        {
            path[i] = BACKWARD_SLASH;
        }
    }
}

void PathUtil::toUnixSlash(string& path)
{
    for (size_t i = 0; i < path.size(); ++ i)
    {
        if (path[i] == BACKWARD_SLASH)
        {
            path[i] = FORWARD_SLASH;
        }
    }
}


bool PathUtil::remove(const string& path)
{
	return (0 == ::remove(path.c_str()));
}